from pydantic import BaseModel, Field
from typing import Optional, Union, Any
from app.utils.custom_exc import CustomException
from fastapi import Query


class FilterBaseModel(BaseModel):
    name: str = Field(description='名字')
    source_filed: Optional[str] = Field(description='目标字段')
    lookup_expr: Optional[str] = Field(description='查找方式', default='eq')
    label: Optional[str] = Field(description='标签')
    type: Optional[Any] = Field(description='类型', default=str)
    method: Optional[Any] = Field(description='执行的方法')
    required: Optional[bool] = Field(description='请求', default=False)


class FilterListModel(BaseModel):
    data: list[FilterBaseModel] = Field(description='数据')


def li_to_model_to_dict(li):
    dit = {'data': li}
    ret = FilterListModel.validate(dit)
    return ret.dict()['data']


class OrderBy(object):
    """
    排序
    """
    def __init__(self, queryset, model, order_by=None):
        self.queryset = queryset
        self.model = model
        self.order_by = order_by

    @property
    def data(self):
        queryset = self.queryset
        if self.order_by:
            data = self._parse_order_by()
            queryset = self.queryset.order_by(*data)
        return queryset

    def _parse_order_by(self):
        """
        解析排序
        :return:
        """
        li = []
        for ord in list(self.order_by):
            if ord.find('-') == -1:
                data = self.__by_model(ord)
                if data:
                    li.append(data.asc())
            else:
                ord = ord[1:]
                data = self.__by_model(ord)
                if data:
                    li.append(data.desc())
        return tuple(li)

    def __by_model(self, key):
        """
        排序时获取字段
        :return:
        """
        return getattr(self.model, key)


class Filters(object):
    """
    过滤
    """
    filer_query = frozenset(['gt', 'ge', 'lt', 'le', 'ne', 'eq', 'ic', 'ni', 'in'])

    def __init__(self, queryset, model, req_data, filter_list=None, order_by=None):
        if filter_list is None:
            filter_list = []
        self.queryset = queryset
        self.model = model
        self.req_data = req_data
        self.filter_list = li_to_model_to_dict(filter_list)
        self.order_by = order_by

        self._operator_funcs = {
            'gt': self.__gt_model,
            'ge': self.__ge_model,
            'lt': self.__lt_model,
            'le': self.__le_model,
            'ne': self.__ne_model,
            'eq': self.__eq_model,
            'ic': self.__ic_model,
            'ni': self.__ni_model,
            # 'by': self.__by_model,
            'in': self.__in_model,
        }

    # @property
    # def _filter_data(self):
    #
    #
    #     search_dict = {}
    #     for fit in self.filter_list:
    #         val = self.req_data.get(fit)
    #         key = fit.split('__')[0]
    #         if val and hasattr(self.model, key):
    #             search_dict[fit] = val
    #     return search_dict

    def _filter(self):
        queryset = self.queryset
        for item in self.filter_list:
            if item['required'] and item['name'] not in self.req_data.keys():
                raise CustomException(f'请提交字段{item["name"]}')
            else:
                if item['name'] in self.req_data.keys():
                    values = self.req_data.get(item['name'])
                    try:
                        values = item['type'](values)
                    except:
                        raise CustomException(f'请提交正确字段{item["name"]}')
                    if item['method'] is not None:
                        queryset = item['method'](queryset, item['name'], values)
                    else:
                        # print(item['source_filed'], type(item['source_filed']))
                        data = self._operator_funcs[item['lookup_expr']](key=item['source_filed'], value=values)
                        queryset = queryset.filter(data)
        return queryset
        # li = []
        # for search_key, value in self._filter_data.items():
        #     key, ope = search_key.split('__')
        #     if ope in self.filer_query:
        #         data = self._operator_funcs[ope](key=key, value=value)
        #         li.append(data)
        # return li

    @property
    def data(self):
        return self._filter()

    def pagination_class(self, page_num=1, page_size=10, max_page_size=50, error_out=False):
        pagin = self.data.paginate(
            page=page_num,
            per_page=page_size,
            error_out=error_out,
            max_per_page=max_page_size
        )
        return pagin.items, pagin.total

    def __gt_model(self, key, value):
        """
        大于
        :param key:
        :param value:
        :return:
        """
        return getattr(self.model, key) > value

    def __ge_model(self, key, value):
        """
        大于等于
        :param key:
        :param value:
        :return:
        """
        return getattr(self.model, key) >= value

    def __lt_model(self, key, value):
        """
        小于
        :param key:
        :param value:
        :return:
        """
        return getattr(self.model, key) < value

    def __le_model(self, key, value):
        """
        小于等于
        :param key:
        :param value:
        :return:
        """
        return getattr(self.model, key) <= value

    def __eq_model(self, key, value):
        """
        等于
        :param key:
        :param value:
        :return:
        """
        return getattr(self.model, key) == value

    def __ne_model(self, key, value):
        """
        不等于
        :param key:
        :param value:
        :return:
        """
        return getattr(self.model, key) != value

    def __ic_model(self, key, value):
        """
        包含
        :param key:
        :param value:
        :return:
        """
        return getattr(self.model, key).like('%{}%'.format(value))

    def __ni_model(self, key, value):
        """
        不包含
        :param key:
        :param value:
        :return:
        """
        return getattr(self.model, key).notlike('%{}%'.format(value))

    def __in_model(self, key, value):
        """
        查询多个相同字段的值
        :param key:
        :param value:
        :return:
        """
        return getattr(self.model, key).in_(value)


if __name__ == '__main__':
    asl = [
        {'name': 'name', 'source_filed': 'name', 'lookup_expr': 'eq', 'label': '包含名字', 'type': str, 'method': None, 'required': False},
        {'name': 'id', 'source_filed': 'id', 'lookup_expr': 'eq', 'label': '等于', 'type': str},
    ]
    retu = li_to_model_to_dict(asl)
    print(retu)
